﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using FirstFloor.Documents.Fonts;

namespace FirstFloor.Documents.Pdf.Fonts
{
    /// <summary>
    /// Maps standard fonts to glyph typefaces available on the local system.
    /// </summary>
    internal static class StandardFonts
    {
        private static Dictionary<string, string> fontNameMap = InitializeFontNameMap();
        private static Dictionary<string, string[]> fontFileNameMap = InitializeFontFileNameMap();
        private static GlyphTypefaceInfo fallbackTypeface = CreateFallbackTypeface();
        private static bool initialized = false;

        /// <summary>
        /// Maps alternative standard font names to the default standard font name
        /// </summary>
        /// <returns></returns>
        private static Dictionary<string, string> InitializeFontNameMap()
        {
            var names = new Dictionary<string, string>();
            AddFontNames(names, "Courier", "CourierNew", "CourierNewPSMT");
            AddFontNames(names, "Courier-Bold", "CourierNew,Bold", "Courier,Bold", "CourierNewPS-BoldMT", "CourierNew-Bold");
            AddFontNames(names, "Courier-Oblique", "CourierNew,Italic", "Courier,Italic", "CourierNewPS-ItalicMT", "CourierNew-Italic");
            AddFontNames(names, "Courier-BoldOblique", "CourierNew,BoldItalic", "Courier,BoldItalic", "CourierNewPS-BoldItalicMT", "CourierNew-BoldItalic");
            AddFontNames(names, "Helvetica", "ArialMT", "Arial");
            AddFontNames(names, "Helvetica-Bold", "Arial-BoldMT", "Arial,Bold", "Arial-Bold", "Helvetica,Bold");
            AddFontNames(names, "Helvetica-Oblique", "Arial-ItalicMT", "Arial,Italic", "Arial-Italic", "Helvetica,Italic", "Helvetica-Italic");
            AddFontNames(names, "Helvetica-BoldOblique", "Arial-BoldItalicMT", "Arial,BoldItalic", "Arial-BoldItalic", "Helvetica,BoldItalic", "Helvetica-BoldItalic");
            AddFontNames(names, "Times-Roman", "TimesNewRomanPSMT", "TimesNewRoman", "TimesNewRomanPS");
            AddFontNames(names, "Times-Bold", "TimesNewRomanPS-BoldMT", "TimesNewRoman,Bold", "TimesNewRomanPS-Bold", "TimesNewRoman-Bold");
            AddFontNames(names, "Times-Italic", "TimesNewRomanPS-ItalicMT", "TimesNewRoman,Italic", "TimesNewRomanPS-Italic", "TimesNewRoman-Italic");
            AddFontNames(names, "Times-BoldItalic", "TimesNewRomanPS-BoldItalicMT", "TimesNewRoman,BoldItalic", "TimesNewRomanPS-BoldItalic", "TimesNewRoman-BoldItalic");
            AddFontNames(names, "Symbol");
            AddFontNames(names, "ZapfDingbats");
            return names;
        }

        private static GlyphTypefaceInfo CreateFallbackTypeface()
        {
            GlyphTypefaceInfo typeface;
            if (GlyphTypefaceInfo.TryGetTypeface("cour.ttf", out typeface)) {
                return typeface;
            }

            return null;
        }

        private static void AddFontNames(Dictionary<string, string> names, string name, params string[] alternativeNames)
        {
            names[name] = name;
            foreach (var n in alternativeNames) {
                names[n] = name;
            }
        }


        /// <summary>
        /// Maps standard font names to font file names.
        /// </summary>
        /// <returns></returns>
        private static Dictionary<string, string[]> InitializeFontFileNameMap()
        {
            var result = new Dictionary<string, string[]>();

            result["Courier"] = new string[] { "cour.ttf" };
            result["Courier-Bold"] = new string[] { "courbd.ttf" };
            result["Courier-Oblique"] = new string[] { "couri.ttf" };
            result["Courier-BoldOblique"] = new string[] { "courbi.ttf" };
            result["Helvetica"] = new string[] { "arial.ttf" };
            result["Helvetica-Bold"] = new string[] { "arialbd.ttf" };
            result["Helvetica-Oblique"] = new string[] { "ariali.ttf" };
            result["Helvetica-BoldOblique"] = new string[] { "arialbi.ttf" };
            result["Times-Roman"] = new string[] { "times.ttf" };
            result["Times-Bold"] = new string[] { "timesbd.ttf" };
            result["Times-Italic"] = new string[] { "timesi.ttf" };
            result["Times-BoldItalic"] = new string[] { "timesbi.ttf" };
            result["Symbol"] = new string[] { "symbol.ttf" };
            result["ZapfDingbats"] = new string[] { "zapfdingbats.ttf", "wingding.ttf" };

            return result;
        }

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        public static void Initialize()
        {
            // nothing here, just a way to ensure static ctor is executed
            if (!initialized) {
                initialized = true;
            }
        }

        /// <summary>
        /// Gets the typeface that is used when all else fails.
        /// </summary>
        /// <value>The fallback typeface.</value>
        public static GlyphTypefaceInfo FallbackTypeface
        {
            get { return fallbackTypeface; }
        }

        /// <summary>
        /// Determines whether specified font name is a Standard Type 1 font name.
        /// </summary>
        /// <param name="fontName">Name of the font.</param>
        /// <returns>
        /// 	<c>true</c> if the name is a standard font name; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsStandardFontName(string fontName)
        {
            return fontNameMap.ContainsKey(fontName);
        }

        /// <summary>
        /// Gets the typeface for given standard font name
        /// </summary>
        /// <param name="fontName">Name of the font.</param>
        /// <returns></returns>
        public static GlyphTypefaceInfo GetGlyphTypeface(string fontName)
        {
            // first get default standard font name
            if (!fontNameMap.TryGetValue(fontName, out fontName)) {
                // fall back to times roman
                fontName = "Times-Roman";
            }

            // then map font name to font file names
            string[] fontFileNames;
            if (fontFileNameMap.TryGetValue(fontName, out fontFileNames)) {
                // finally find type with given font file names
                return GetGlyphTypeface(fontFileNames);
            }

            // all failed, return fallback typeface
            return FallbackTypeface;
        }

        private static GlyphTypefaceInfo GetGlyphTypeface(string[] fontFileNames)
        {
            GlyphTypefaceInfo typeface = null;
            foreach (var fontFileName in fontFileNames) {
                if (GlyphTypefaceInfo.TryGetTypeface(fontFileName, out typeface)) {
                    break;
                }
            }

            if (typeface == null){
                typeface = FallbackTypeface;
            }
            return typeface;
        }
    }
}
